/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.common.utils;

import vip.dexian.common.CommonAttributes;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.util.Assert;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.TimeZone;

/**
 * json工具类，基于jackson。
 *
 * @author 挺好的 2023年05月30日 8:28
 */
public final class JsonUtils {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {

        // 设置为默认时区，
        OBJECT_MAPPER.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        // 设置日期格式
        OBJECT_MAPPER.setDateFormat(new SimpleDateFormat(CommonAttributes.DatePattern.DATE_TIME_1));
        // 设置简体中文
        OBJECT_MAPPER.setLocale(Locale.SIMPLIFIED_CHINESE);
        // 忽略未知属性
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // null 值不显示
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        // 小写驼峰样式LOWER_CAMEL_CASE
        OBJECT_MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);

        // 禁止转换为时间戳
        OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 注册时间
        OBJECT_MAPPER.registerModule(new JavaTimeModule());

    }

    private JsonUtils () {
    }

    /**
     * 将指定的value转换为json
     *
     * @param value
     *         要转换的值
     *
     * @return json数据
     *
     * @throws IllegalArgumentException
     *         如果value == null
     * @throws RuntimeException
     *         转换的过程中出现错误
     */
    public static String toJson (Object value) {
        Assert.notNull(value, "value is not null");

        try {
            return OBJECT_MAPPER.writeValueAsString(value);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 将指定的json转换为对象
     *
     * @param json
     *         待转换的数据
     * @param valueType
     *         目标对象的类型
     * @param <T>
     *         目标类型
     *
     * @return 转换后的结果
     *
     * @throws IllegalArgumentException
     *         如果json == null || valueType == null
     * @throws RuntimeException
     *         转换的过程中出现错误
     */
    public static <T> T toObject (String json, Class <T> valueType) {
        Assert.hasText(json, " json must be not null");
        Assert.notNull(valueType, " value type must be not null");

        try {
            return OBJECT_MAPPER.readValue(json, valueType);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 将指定的json转换为对象
     *
     * @param json
     *         待转换的数据
     * @param typeReference
     *         类型引用，可能是一个实体、也可能是一个集合
     * @param <T>
     *         目标类型
     *
     * @return 转换后的结果
     *
     * @throws IllegalArgumentException
     *         如果json == null || valueType == null
     * @throws RuntimeException
     *         转换的过程中出现错误
     */
    public static <T> T toObject (String json, TypeReference <T> typeReference) {
        Assert.hasText(json, " json must be not null");
        Assert.notNull(typeReference, " typeReference must be not null");

        try {
            return OBJECT_MAPPER.readValue(json, typeReference);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 将指定的json转换为对象
     *
     * @param json
     *         待转换的数据
     * @param javaType
     *         数据类型
     * @param <T>
     *         目标类型
     *
     * @return 转换后的结果
     *
     * @throws IllegalArgumentException
     *         如果json == null || valueType == null
     * @throws RuntimeException
     *         转换的过程中出现错误
     */
    public static <T> T toObject (String json, JavaType javaType) {
        Assert.hasText(json, " json must be not null");
        Assert.notNull(javaType, "javaType must be not null");
        try {
            return OBJECT_MAPPER.readValue(json, javaType);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 将指定的json转换为jackson树
     *
     * @param json
     *         待转换的数据
     *
     * @return 转换后的结果
     *
     * @throws IllegalArgumentException
     *         如果json == null || valueType == null
     * @throws RuntimeException
     *         转换的过程中出现错误
     */
    public static JsonNode toTree (String json) {
        Assert.hasText(json, " json must be not null");

        try {
            return OBJECT_MAPPER.readTree(json);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 将指定的json转换为jackson树
     *
     * @param writer
     *         写入字符流
     * @param value
     *         值
     *
     * @throws IllegalArgumentException
     *         如果json == null || valueType == null
     * @throws RuntimeException
     *         ({@link java.io.IOException}，
     *         写入过程过如果出现错误
     */
    public static void writeValue (Writer writer, Object value) {
        Assert.notNull(writer, "writer must be not null");
        Assert.notNull(value, " value must be not null");
        try {
            OBJECT_MAPPER.writeValue(writer, value);
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 构造
     *
     * @param type
     *         类型
     *
     * @return {@link com.fasterxml.jackson.databind.JavaType}
     */
    public static JavaType constructType (Type type) {
        Assert.notNull(type, "type must be not null");

        return TypeFactory.defaultInstance().constructType(type);
    }

    /**
     * 构造
     *
     * @param typeReference
     *         类型引用
     *
     * @return {@link com.fasterxml.jackson.databind.JavaType}
     */
    public static JavaType constructType (TypeReference <?> typeReference) {
        Assert.notNull(typeReference, "typeReference must be not null");

        return TypeFactory.defaultInstance().constructType(typeReference);
    }
}
